#include "dm9000.h"

#ifndef TRUE
	#define TRUE         1
#endif
#ifndef FALSE
	#define FALSE        0
#endif

#define DM9000_NUM		  2
#define DM9000_DEBUG	  0

typedef const struct 
{
	U16	xPort : 3,
			xPin  : 4,
			xMode : 8,
			xSta  : 1;
	U8 xSpeed;
}t_dm9000map;

extern t_dm9000map DM9000Map[];

extern U8 port_dm_irq;				
extern U8 pin_dm_irq;

extern U32 DM9000_BASE_ADR;
extern U32 DM9000_CMD_ADR;
extern U32 DM9000_DATA_ADR;
extern U8 DM9000_BUS_WIDTH;

static U8 tx_busy = 0;
static int errCnt = 0;
static int rxCnt = 0;

extern int eth_handle (void* pData,int nLen);
extern U8 eth_buf[];
extern U8 own_hw_adr[];
extern U32 uneth_mtu;



#define DM9000_CFG(X) gt_gpio_cfg(DM9000Map[(X)].xPort,DM9000Map[(X)].xPin,DM9000Map[(X)].xMode,DM9000Map[(X)].xSta,DM9000Map[(X)].xSpeed)
#define DM9000_GET(X) gt_gpio_get(DM9000Map[(X)].xPort,DM9000Map[(X)].xPin)
#define DM9000_SET(X,V) gt_gpio_set(DM9000Map[(X)].xPort,DM9000Map[(X)].xPin,(V))
#define DM9000_IRQPORT	port_dm_irq
#define DM9000_IRQPIN   pin_dm_irq


#if DM9000_DEBUG
#define DM9000_PRINTF	gt_printf	//UART_SEND(0,(x),strlen(x))
#define DM9000_TRACE	gt_printf	//UART_SEND(0,(x),strlen(x))
#else
#define DM9000_PRINTF(...)
#define DM9000_TRACE(...)
#endif

//Private Defines
#define DM9000_PHY			0x40    /* PHY address 0x01 */

#define MAX_ADDR_LEN 		6

#define DM9000_ID			0x90000A46  /* DM9000 ID */
#define DM9000_PKT_MAX		1536	    /* Received packet max size */
#define DM9000_PKT_RDY		0x01	    /* Packet ready to receive */

#define DM9000_NCR			0x00
#define DM9000_NSR			0x01
#define DM9000_TCR			0x02
#define DM9000_TSR1			0x03
#define DM9000_TSR2			0x04
#define DM9000_RCR			0x05
#define DM9000_RSR			0x06
#define DM9000_ROCR			0x07
#define DM9000_BPTR			0x08
#define DM9000_FCTR			0x09
#define DM9000_FCR			0x0A
#define DM9000_EPCR			0x0B
#define DM9000_EPAR			0x0C
#define DM9000_EPDRL		0x0D
#define DM9000_EPDRH		0x0E
#define DM9000_WCR			0x0F

#define DM9000_PAR			0x10
#define DM9000_MAR			0x16

#define DM9000_GPCR			0x1E
#define DM9000_GPR			0x1F
#define DM9000_TRPAL		0x22
#define DM9000_TRPAH		0x23
#define DM9000_RWPAL		0x24
#define DM9000_RWPAH		0x25

#define DM9000_VIDL			0x28
#define DM9000_VIDH			0x29
#define DM9000_PIDL			0x2A
#define DM9000_PIDH			0x2B

#define DM9000_CHIPR		0x2C
#define DM9000_TCR2			0x2D
#define DM9000_OTCR			0x2E
#define DM9000_SMCR			0x2F

#define DM9000_ETCR			0x30	/* early transmit control/status register */
#define DM9000_CSCR			0x31	/* check sum control register */
#define DM9000_RCSSR		0x32	/* receive check sum status register */

#define DM9000_LEDCR		0x34
#define DM9000_BUSCR		0x38
#define DM9000_INTCR		0x39
#define DM9000_SCCR			0x05

#define DM9000_MRCMDX		0xF0
#define DM9000_MRCMD		0xF2
#define DM9000_MRRL			0xF4
#define DM9000_MRRH			0xF5
#define DM9000_MWCMDX		0xF6
#define DM9000_MWCMD		0xF8
#define DM9000_MWRL			0xFA
#define DM9000_MWRH			0xFB
#define DM9000_TXPLL		0xFC
#define DM9000_TXPLH		0xFD
#define DM9000_ISR			0xFE
#define DM9000_IMR			0xFF

#define CHIPR_DM9000A		0x19
#define CHIPR_DM9000C		0x1A
#define CHIPR_DM9000B		0x1B

#define NCR_EXT_PHY         (1<<7)
#define NCR_WAKEEN          (1<<6)
#define NCR_FCOL            (1<<4)
#define NCR_FDX             (1<<3)
#define NCR_LBK             (3<<1)
#define NCR_RST             (1<<0)

#define NSR_SPEED           (1<<7)
#define NSR_LINKST          (1<<6)
#define NSR_WAKEST          (1<<5)
#define NSR_TX2END          (1<<3)
#define NSR_TX1END          (1<<2)
#define NSR_RXOV            (1<<1)

#define TCR_TJDIS           (1<<6)
#define TCR_EXCECM          (1<<5)
#define TCR_PAD_DIS2        (1<<4)
#define TCR_CRC_DIS2        (1<<3)
#define TCR_PAD_DIS1        (1<<2)
#define TCR_CRC_DIS1        (1<<1)
#define TCR_TXREQ           (1<<0)

#define TSR_TJTO            (1<<7)
#define TSR_LC              (1<<6)
#define TSR_NC              (1<<5)
#define TSR_LCOL            (1<<4)
#define TSR_COL             (1<<3)
#define TSR_EC              (1<<2)

#define RCR_WTDIS           (1<<6)
#define RCR_DIS_LONG        (1<<5)
#define RCR_DIS_CRC         (1<<4)
#define RCR_ALL             (1<<3)
#define RCR_RUNT            (1<<2)
#define RCR_PRMSC           (1<<1)
#define RCR_RXEN            (1<<0)

#define RSR_RF              (1<<7)
#define RSR_MF              (1<<6)
#define RSR_LCS             (1<<5)
#define RSR_RWTO            (1<<4)
#define RSR_PLE             (1<<3)
#define RSR_AE              (1<<2)
#define RSR_CE              (1<<1)
#define RSR_FOE             (1<<0)

#define FCTR_HWOT(ot)		(((ot) & 0x0F ) << 4)
#define FCTR_LWOT(ot)		((ot) & 0x0F )

#define IMR_PAR             (1<<7)
#define IMR_ROOM            (1<<3)
#define IMR_ROM             (1<<2)
#define IMR_PTM             (1<<1)
#define IMR_PRM             (1<<0)

#define ISR_ROOS            (1<<3)
#define ISR_ROS             (1<<2)
#define ISR_PTS             (1<<1)
#define ISR_PRS             (1<<0)
#define ISR_CLR_STATUS      (ISR_ROOS | ISR_ROS | ISR_PTS | ISR_PRS)

#define EPCR_REEP           (1<<5)
#define EPCR_WEP            (1<<4)
#define EPCR_EPOS           (1<<3)
#define EPCR_ERPRR          (1<<2)
#define EPCR_ERPRW          (1<<1)
#define EPCR_ERRE           (1<<0)

#define GPCR_GEP_CNTL       (1<<0)


enum DM9000_PHY_mode
{
    DM9000_10MHD = 0, DM9000_100MHD = 1,
    DM9000_10MFD = 4, DM9000_100MFD = 5,
    DM9000_AUTO  = 8, DM9000_1M_HPNA = 0x10
};

enum DM9000_TYPE
{
    TYPE_DM9000E,
    TYPE_DM9000A,
    TYPE_DM9000B
};


//Private Macros
#define dm9000_Rst(x)		DM9000_SET(0,(x))

//Private Variables	
/* Read a byte from I/O port */
U32 gt_dm9000_regread(U32 reg)
{

	__raw_writew(reg, DM9000_CMD_ADR);
	return __raw_readw(DM9000_DATA_ADR);
}

/* Write a byte to I/O port */
void gt_dm9000_regwrite(U32 reg, U32 value)
{

	__raw_writew(reg, DM9000_CMD_ADR);
	__raw_writew(value, DM9000_DATA_ADR);
}


/* Write a word to phyxcer */
static void phy_write(U32 reg, U32 value)
{
	U32 i = 100000;

	/* Fill the phyxcer register into REG_0C */
	gt_dm9000_regwrite(DM9000_EPAR, DM9000_PHY | reg);

	/* Fill the written data into REG_0D & REG_0E */
	gt_dm9000_regwrite(DM9000_EPDRL, (value & 0xff));
	gt_dm9000_regwrite(DM9000_EPDRH, ((value >> 8) & 0xff));
	gt_dm9000_regwrite(DM9000_EPCR, 0x0A);	/* Issue phyxcer write command */

	while ((gt_dm9000_regread(DM9000_EPCR) & BIT_MASK(0)) && i--);/* Wait write complete */

	gt_dm9000_regwrite(DM9000_EPCR, 0x08);	/* Clear phyxcer write command */
}

/* Set PHY operationg mode */
static void phy_mode_set(U32 media_mode)
{
	U32 phy_reg4 = 0x01e1, phy_reg0 = 0x1000;

	if (!(media_mode & DM9000_AUTO)) {
		switch (media_mode) {
		case DM9000_10MHD:
			phy_reg4 = 0x21;
			phy_reg0 = 0x0000;
			break;
		case DM9000_10MFD:
			phy_reg4 = 0x41;
			phy_reg0 = 0x1100;
			break;
		case DM9000_100MHD:
			phy_reg4 = 0x81;
			phy_reg0 = 0x2000;
			break;
		case DM9000_100MFD:
			phy_reg4 = 0x101;
			phy_reg0 = 0x3100;
			break;
		}
		phy_write(4, phy_reg4);	/* Set PHY media mode */
		phy_write(0, phy_reg0);	/*  Tmp */
	}

	gt_dm9000_regwrite(DM9000_GPR, 0x00);	/* Enable PHY */
}


int gt_dm9000_reset(void)
{
	U32 i;
	U32 value;

	/* GPIO Initial */
	//i=DM9000_NUM;
	//while(i--)DM9000_CFG(i);

	/* Reset DM9000,
	   see DM9000 Application Notes V1.22 Jun 11, 2004 page 29 */
	/* Hardware Reset */
	dm9000_Rst(0);
	gt_us_delay(2000);
	dm9000_Rst(1);
	gt_us_delay(5000);
	/* DEBUG: Make all GPIO pins outputs */
	gt_dm9000_regwrite(DM9000_GPCR, 0x0F);
	/* Step 1: Power internal PHY by writing 0 to GPIO0 pin */
	gt_dm9000_regwrite(DM9000_GPR, 0);
	/* Step 2: Software reset */
	gt_dm9000_regwrite(DM9000_NCR, 3);

	while (gt_dm9000_regread(DM9000_NCR) & NCR_RST)
		gt_us_delay(100);		/* delay 10us */

	/* identify DM9000 */
	value  = gt_dm9000_regread(DM9000_VIDL);
	value |= gt_dm9000_regread(DM9000_VIDH) << 8;
	value |= gt_dm9000_regread(DM9000_PIDL) << 16;
	value |= gt_dm9000_regread(DM9000_PIDH) << 24;
	if (value != DM9000_ID)
	    return FALSE;
	DM9000_TRACE("dm9000 id: 0x%x\n", value);

	/* Set PHY */
	phy_mode_set(DM9000_AUTO);

	/* Program operating register */
	gt_dm9000_regwrite(DM9000_NCR, 0x00);	/* only intern phy supported by now */
	gt_dm9000_regwrite(DM9000_TCR, 0x00);	/* TX Polling clear */
	gt_dm9000_regwrite(DM9000_BPTR, 0x3F);	/* Less 3Kb, 200us */
	gt_dm9000_regwrite(DM9000_FCTR, FCTR_HWOT(3) | FCTR_LWOT(8));	/* Flow Control : High/Low Water */
	gt_dm9000_regwrite(DM9000_FCR, 0x00);	/* SH FIXME: This looks strange! Flow Control */
	gt_dm9000_regwrite(DM9000_SMCR, 0x00);	/* Special Mode */
	gt_dm9000_regwrite(DM9000_NSR, NSR_WAKEST | NSR_TX2END | NSR_TX1END);	/* clear TX status */
	gt_dm9000_regwrite(DM9000_ISR, 0x0F);	/* Clear interrupt status */

	/* set mac address */
	for (i = 0x10; i < 0x16; i++)
		gt_dm9000_regwrite(i, own_hw_adr[i - 0x10]);
	/* set multicast address */
	for (i = 0x16; i < 0x1E; i++)
		gt_dm9000_regwrite(i, 0xFF);

	/* Activate DM9000 */
	gt_dm9000_regwrite(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);	/* RX enable */
#if 1//DM9000_INT_ENABLE
	gt_dm9000_regwrite(DM9000_IMR, IMR_PAR | IMR_PRM);	/* Enable RX interrupt mask */
	// (tbl_bspDm9000[0] + 1)->pin, tbl_bspDm9000[0] + 1
	//return EX_IRQ_CONFIG(DM9000_IRQPORT,DM9000_IRQPIN, IRQ_MODE_LOWEDGE, NULL);
#else
	gt_dm9000_regwrite(DM9000_IMR, IMR_PAR); /* Disable RX interrupt mask */
#endif
	return TRUE;

}



int gt_dm9000_init(void)
{
	U32 i;	
	i=DM9000_NUM;
	while(i--)DM9000_CFG(i);
	i = EX_IRQ_CONFIG(DM9000_IRQPORT,DM9000_IRQPIN, IRQ_MODE_LOWEDGE, NULL);//IRQ_MODE_HIGHEDGE
	return i;
}

void gt_dm9000_writecmd(U32 cmd)
{
	__raw_writew(cmd, DM9000_CMD_ADR);
}

void gt_dm9000_writedat(U32 dat)
{
	__raw_writew(dat, DM9000_DATA_ADR);
}
U32 gt_dm9000_readdat(void)
{
	return __raw_readw(DM9000_DATA_ADR);
}

U32 gt_dm9000_readcmd(void)
{
	return __raw_readw(DM9000_CMD_ADR);
}


int gt_eth_init(void)
{
	int itry=2;
	/* Initialize the ETH ethernet controller. */
	while(gt_dm9000_reset()!=1)
	{
		if(itry--)continue;
		DM9000_PRINTF("Ethernet error!\n");
	}	
	return 1;
}


void ETH_IRQHandler (void) 
{
	U16 last_io,int_status;
  /* Ethernet Controller Interrupt function. */
	last_io = gt_dm9000_readcmd();

	/* Disable all interrupts */
	gt_dm9000_regwrite(DM9000_IMR, IMR_PAR);
	
	/* Got DM9000 interrupt status */
	int_status = gt_dm9000_regread(DM9000_ISR);               /* Got ISR */
	gt_dm9000_regwrite(DM9000_ISR, int_status);    /* Clear ISR status */

	/* receive overflow */
	if (int_status & ISR_ROS)
	{
		DM9000_PRINTF("overflow\n");
		gt_dm9000_regwrite(DM9000_NCR, NCR_RST);
// 		return;
	}

	if (int_status & ISR_ROOS)
	{
		DM9000_PRINTF("counter overflow\n");
		gt_dm9000_regwrite(DM9000_NCR, NCR_RST);
// 		return;
	}

	/* Received the coming packet */
	if (int_status & ISR_PRS)
	{
		U32 nSte, nLen ,nStatus;
		int nSendMaxLen,i;
		U16* pSrc=(U16*)eth_buf;
// 		U8  pTemp[4];
		do
		{
			/* Check packet ready or not */
			gt_dm9000_regread(DM9000_MRCMDX);				/* Dummy read */
			nSte = gt_dm9000_readdat() & 0xFF;		/* Got most updated pData */
			if (nSte == 0)
			{
				//DM9000_PRINTF("dm9000 rx:nSte == 0\n");
				//gt_dm9000_regwrite(DM9000_NCR, NCR_RST);
				break;
			}
			if (nSte > 1) 
			{
				DM9000_PRINTF("dm9000 rx: rx error, stop device\n");
				gt_dm9000_regwrite(DM9000_RCR, 0x00);	/* Stop Device */
				gt_dm9000_regwrite(DM9000_ISR, 0x80);	/* Stop INT request */				
// 			gt_dm9000_reset();	
				break;
			}
			/* A packet ready now  & Get status/length */
			gt_dm9000_writecmd(DM9000_MRCMD);

			nStatus = gt_dm9000_readdat();
			nLen = gt_dm9000_readdat();

			//DM9000_PRINTF("dm9000 rx: status %04x nLen %d\n", nStatus, nLen);

			if ((nStatus & 0xB700) || (nLen < 0x40) || (nLen > DM9000_PKT_MAX)) 
			{
				DM9000_PRINTF("rx err status: %04x\n",nStatus);
				if (nStatus & 0x0100)DM9000_PRINTF("rx fifo error\n");
				if (nStatus & 0x0200)DM9000_PRINTF("rx crc error\n");
				if (nStatus & 0x8000)DM9000_PRINTF("rx length error\n");
				if (nLen > DM9000_PKT_MAX) 
				{
					DM9000_PRINTF("rx length too big\n");
					/* RESET device */
					gt_dm9000_reset();
					break;
				}
				if(errCnt++ > 3)
				{  	
					errCnt = 0;
					DM9000_PRINTF("Too many error,dm9000 Reset!\n", nStatus, nLen);
					gt_dm9000_reset();
					break;	
				}
				else
				{
					DM9000_PRINTF("rx NCR_RST,%04x\n",nStatus);
					gt_dm9000_regwrite(DM9000_NCR, NCR_RST);
				}
				break;
			}
			while(nLen>0)
			{
				//if (nLen > ETH_MTU)nSendMaxLen = ETH_MTU;
				if (nLen > uneth_mtu)nSendMaxLen = uneth_mtu;
				else nSendMaxLen = nLen;
				nLen-=nSendMaxLen;
				for(i=0;i<((nSendMaxLen+1)/2);i++)
				{
					pSrc[i]=gt_dm9000_readdat();//DM9000_PRINTF("%c%c",pSrc[i]&0xff,(pSrc[i]>>8));					
				}
				if(!eth_handle(pSrc,nSendMaxLen))
				{
					DM9000_PRINTF("eth_handle:0x%08x,%d\n",pSrc,nSendMaxLen);
				}
			}			
		}while(nSte==1);//(0);//		
	}

	/* Transmit Interrupt check */
	if (int_status & ISR_PTS)
	{
		DM9000_PRINTF("[tx Done]");
	}
	else
	{
		if(!tx_busy&&rxCnt++>100)
		{
			rxCnt = 0;
			gt_dm9000_regwrite(DM9000_NCR, NCR_RST);
		}
	}

	/* Re-enable interrupt mask IMR_PTM |*/
	gt_dm9000_regwrite(DM9000_IMR, IMR_PAR | IMR_PRM);

	gt_dm9000_writecmd(last_io);
}



int gt_eth_send(const unsigned char* buf,int size)
{
	/* Send frame to ETH ethernet controller */
	U16* pData=(U16*)buf;
	U16 nLen=size;
	long i = 1000000;
	
	/* lock DM9000 device */
	if(tx_busy)	return 0;
	tx_busy = 1;
		
	/* Wait transmit done */
	DM9000_PRINTF("Wait transmit done\n");
	while ((gt_dm9000_regread(DM9000_TCR) & TCR_TXREQ));	  /* Got TX status */

	/* Move data to DM9000 TX RAM */
	gt_dm9000_writecmd(DM9000_MWCMD);
	
	/* Write data into dm9000a, two bytes at a time
	* Handling pbuf's with odd number of bytes correctly
	* No attempt to optimize for speed has been made */
	for (i = (((nLen) + 1) & ~1); i; i -= 2)
		gt_dm9000_writedat(*pData++);

	gt_dm9000_regwrite(DM9000_TXPLL, nLen & 0xFF);
	gt_dm9000_regwrite(DM9000_TXPLH, (nLen >> 8) & 0xFF);
	
	/* Issue TX polling command */
	gt_dm9000_regwrite(DM9000_TCR, TCR_TXREQ);	/* Cleared after TX complete */
	DM9000_PRINTF("transmit process\n");

	/* unlock DM9000 device */
	tx_busy = 0;
	
	return 1;
}

int gt_eth_set(int flag)
{
	if(flag)
	{
		gt_dm9000_regwrite(DM9000_IMR, IMR_PAR | IMR_PRM);	/* Enable RX interrupt mask */
		gt_dm9000_regwrite(DM9000_RCR, RCR_DIS_LONG | RCR_DIS_CRC | RCR_RXEN);	/* RX enable */
	}
	else
	{
		gt_dm9000_regwrite(DM9000_IMR, IMR_PAR); /* Disable RX interrupt mask */
		gt_dm9000_regwrite(DM9000_RCR, 0x00);	/* Disable RX */
	}
	return 1;
}


